namespace IBS.Model

open System
open System.Collections.Concurrent

type internal Message<'a> =
    | Check   of 'a
    | Record  of 'a
    | Process of 'a
    | Success of 'a
    | Retry   of 'a * string
    | Failure of 'a * string
    
type MailboxService<'a>(validate: Func<'a, string>, record: Action<'a>, proc: Action<'a>, notify: Action<'a, string>) =
    let supervisor =
        MailboxProcessor.Start(fun inbox ->
            let inline safe f m retry = 
                try f() with ex -> 
                    inbox.Post (if retry then Retry (m, ex.Message) else Failure (m, ex.Message))
            
            let rec run() =
                async {
                    let! msg = inbox.Receive()
                    match msg with
                    | Check m -> 
                        safe (fun () ->
                            let validationResult = validate.Invoke m
                            if String.IsNullOrWhiteSpace validationResult then inbox.Post (Record m)
                            else inbox.Post (Failure (m, validationResult))) m false
                    | Record m -> 
                        safe (fun () -> 
                            record.Invoke m
                            inbox.Post (Process m)) m false
                    | Process m ->
                        safe (fun () ->
                            proc.Invoke m
                            inbox.Post (Success m)) m true
                    | Retry (m, ex1) ->
                        safe (fun () ->
                            proc.Invoke m
                            inbox.Post (Success m)) m false
                    | Success m       -> notify.Invoke (m, null)
                    | Failure (m, ex) -> notify.Invoke (m, ex)
                    return! run()
                }
            run())
                
    member x.Schedule m = supervisor.Post (Check m)
    
